15. It is a composer plugin that comes with
Symfony 4
The idea is automation to the max
when installing and configuring packages
16. Modifies the behaviour of the
require and update commands
Allows Symfony to perform tasks before
or after the composer commands
17. Symfony 4
Your application
with Symfony Flex
composer req mailer
Symfony Flex
Server
Recipe?
No
Regular install
With composer
18. Symfony 4
Your application
with Symfony Flex
composer req mailer
Symfony Flex
Server
Recipe?
Yes
Install them
With composer
Follow recipe instructions
Decide which packages to install
Run any task to configure them
25. Entity
Code that has to do with the model itself.
Doesn’t depend on services, or query the DB.
26. Accessors
In this workshop we are going to use setters and getters:
•getDirector()
•setDirector()
This is not the only way. See for instance:
http://williamdurand.fr/2013/08/07/ddd-with-symfony2-folder-structure-and-code-first/
29. Doctrine Query Language (DQL)
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT m, a FROM App:Movie m LEFT JOIN m.actors a WHERE
m.id = :id'
)->setParameter('id', $movieId);
$movie = $query->getOneOrNullResult();
33. We could do this
for ($i = 0; $i < 10; $i ++) {
$movie = new Movie();
$movie->setYear(1994);
$movie->setName('Pulp Fiction'.$i);
//...
$em->persist($movie);
}
$em->flush();
34. With Alice
AppEntityMovie:
movie{1..10}:
name: '<sentence(4, true)>'
director: '<name()>'
year: '<numberBetween(1970, 2017)>'
picture: '<image("./public/images", 500, 500, "cats", false)>'
See all the generators in
https://github.com/fzaninotto/Faker
39. - Checks if foo is an array and bar an element
- If not, checks if foo is an object and bar a property
- If not, checks if foo is an object and bar a method
- If not, checks if foo is an object and getBar a method
- If not, checks if foo is an object and isBar a method
{{ foo.bar }}
Access variables
40. {% for user in users %}
{{ user.username }}
{% endfor %}
For each
49. Webpack Encore
A wrapper around Webpack that makes it
easier to set up.
It solves a big % of cases.
If you require something specific, you can still
use Webpack without Encore.
52. Forms in Twig
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
You can customise a lot the rendering of every widget,
If you need to.
54. Forms in their own classes
class TaskType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('task')
->add('dueDate', null, array('widget' => 'single_text'))
->add('save', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Task::class,
));
}
}
57. Event
class SaleEvent extends Event
{
const NAME = 'sale.created';
protected $sale;
protected $movie;
protected $numTickets;
public function __construct(Sale $sale, Movie $movie, int $numTickets)
{
$this->sale = $sale;
$this->movie = $movie;
$this->numTickets = $numTickets;
}
public function getSale()
{
return $this->sale;
}
public function getMovie()
{
return $this->movie;
}
public function getNumTickets()
{
return $this->numTickets;
}
}
A bag of parameters
that describe the event
61. Services
A service is an object that does something:
• Generate a thumbnail.
• Generate a PDF.
• A query to ElasticSearch.
• Log something.
• Send an email.
62. Example
class SaleLogger
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function log(Sale $sale)
{
$this->logger->info('Sold ' . count($sale->getTickets()) . ' tickets to ' . $sale->getFullName());
}
}
Where does this come from?
63. Using services
public function listAction(SaleLogger $saleLogger)
{
//…
$saleLogger->log($sale);
}
Is injected with dependencies
72. Constraint
class HasAvailableSeats extends Constraint
{
public $message = 'No {{ number }} available seats’;
protected $movie;
public function __construct($options)
{
$this->movie = $options['movie'];
}
public function getMovie()
{
return $this->movie;
}
public function validatedBy()
{
return get_class($this).'Validator';
}
}
No logic,
data that describes
the constraint
Who validates this?
73. class HasAvailableSeatsValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
$available = $constraint->getMovie()->getAvailableSeats();
if ($value > $available) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ number }}', $value)
->addViolation();
}
}
}
Validator
Validation logic
76. Up to level 2
Good use of HTTP verbs (GET, POST, PUT, DELETE, PATCH…)
Structure based on resources (/movies, /movies/31).
Use of HTTP Status codes (200, 201, 406, …).
Use of representations (JSON, XML, …).
81. Representation in API != DB
{
id: 9,
name: "victoriaq",
password: "encryptedPassword",
email: "victoria@limenius.com",
avatar: "avatar.jpg",
twitter_handler: "vicqr",
profile: {
id: 19,
bio: "My bio."
}
}
I want this to be “username”
I don’t want to expose it!
Only in profile, not in list
We want to prepend “thumb_”
Only in version 2 of the API
I’d like this to be bio:”My bio”
We do this in the normalizer
82. Annotations
MaxDepth
• Detect and limit the serialization depth
• Especially useful when serializing large trees
Groups
• Sometimes, you want to serialize different sets of attributes from your entities
• Groups are a handy way to achieve this need